/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3 as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package br.ufrj.dcc.pushpeer.protocol;

import java.util.ArrayList;
import java.util.List;

import peersim.config.Configuration;
import peersim.core.CommonState;
import peersim.core.Node;

public class PushPeerKnownLeastAppearing extends PushPeer {

	/**
	 * Size of the rarest files list.
	 */
	protected static final String RAREST_LIST_SIZE = "rarest_list_size";
	
	/**
	 * This number gives a weight to the old cache request, to avoid too much cache swapping.
	 */
	public static final int OLD_CACHE_INERTIA = 3;
	
	/**
	 * Holds the last file this peer (as a Cache Bank) requested to it's managed peer.
	 */
	protected int lastRequestedCache = -1;
	
	/**
	 * Holds the number of times the last file appeared in the rarest lists. Files with a high count should be kept for a longer time.
	 */
	protected int lastRequestCount = -1;
	
	/**
	 * Size of the rarest files list.
	 */
	protected int rarest_list_size;
	
	public PushPeerKnownLeastAppearing(String prefix) {
		super(prefix);
		rarest_list_size = (int)(Configuration.getInt(prefix + "." + KNOWN_PEERS_MIN_SIZE));
	}

	/**
	 * This strategy picks the least appearing file on the known peers' rarest files lists.
	 * @param pid PushPeer's protocol id.
	 * @return the file to be requested for cache.
	 */
	@Override
	protected int replicationStrategy(int pid) {
		
		int rarestFile = -1;
		
		if(managedPeers != null) {
			//First, fetch the rarest files from the known peers.
			int numFiles = fileNumPiecesCompleted.size();
			int totalQueriedFiles = 0;
			
			List<Integer> rarest = new ArrayList<Integer>(numFiles+1);
			//Adds one more, because index 0 is not used.
			for(int i=1; i <= numFiles+1; i++)
				rarest.add(0);
			
			for(Node n : getKnownPeers()) {
				List<Integer> rarestPeerFiles = ((PushPeer)n.getProtocol(pid)).getRarestFile(rarest_list_size);
				if(!rarestPeerFiles.isEmpty()) {//Otherwise the peer isn't interested in anything.
					totalQueriedFiles += rarestPeerFiles.size();
					for(Integer i : rarestPeerFiles)
						rarest.set(i, rarest.get(i)+1);
				}
			}
			if(totalQueriedFiles == 0)
				//None of the known peers want a file (should only happen near an equilibrium state).
				return rarestFile;
			
			int appearanceCount = Integer.MAX_VALUE;
			int leastAppearing = -1;
			
			for(int i=1; i <= numFiles; i++)
				if(rarest.get(i) > 0 && rarest.get(i) < appearanceCount) {
					appearanceCount = rarest.get(i);
					leastAppearing = i;
				}
			
			rarestFile = leastAppearing;
			
			double prob = CommonState.r.nextDouble();
			double threshold;
			if(lastRequestedCache != -1) {
				threshold = (lastRequestCount*OLD_CACHE_INERTIA)/(lastRequestCount*OLD_CACHE_INERTIA + appearanceCount);
				if(prob > threshold) {
					rarestFile = lastRequestedCache;
				}
				else {
					lastRequestCount = appearanceCount;
					lastRequestedCache = rarestFile;
				}
			}
			else {
				lastRequestCount = appearanceCount;
				lastRequestedCache = rarestFile;
			}
			
		}
		
		// -1 here means there is no managed peer.
		return rarestFile;
	}

}
